﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Data;
using System.Data.SqlClient;
using JDML.MappingClass;
using JDML.Attributes;
using JDML.FilterCondition;

namespace JDML.StaticExtensions
{
    public static class DAOExtension
    {
        public static SqlConnection Connection { get; set; }
        public static SqlTransaction Transaction { get; set; }

        private static DAOMapping initMappingTable(Object o)
        {
            if (Connection == null)
            {
                throw new Exception("DAOExtension.Connection 不能为 null，必须指定一个数据库连接。");
            }
            Type type = o.GetType();
            DAOMapping mapping = MappingTable.GetMapping(type);
            if (mapping == null)
            {
                mapping = new DAOMapping();
                TableAttribute tableAttribute = getTableAttribute(o);
                List<FieldMapping> fieldmappinglist = getFieldMapping(o);
                if (tableAttribute != null)
                {
                    mapping.DBTableMapping.TableName = string.IsNullOrWhiteSpace(tableAttribute.TableName) ? o.GetType().Name : tableAttribute.TableName;
                    mapping.DBTableMapping.ReloadAfterInsert = tableAttribute.ReloadAfterInsert;
                    mapping.DBTableMapping.ReloadAfterUpdate = tableAttribute.ReloadAfterUpdate;
                }
                else
                {
                    mapping.DBTableMapping.TableName = o.GetType().Name;
                }
                SQLAttribute[] sqlAttributes = getSQLAttributes(o);
                foreach (SQLAttribute sqlattribute in sqlAttributes)
                {
                    SQLCmdProperty sqlcmdproperty = new SQLCmdProperty()
                    {
                        SQLType = sqlattribute.SQLType,
                        SQLCommand = sqlattribute.SQLCommand,
                        SQLName = sqlattribute.SQLName,
                        ReloadAfterInsert = sqlattribute.ReloadAfterInsert,
                        ReloadAfterUpdate = sqlattribute.ReloadAfterUpdate
                    };
                    switch (sqlattribute.SQLType)
                    {
                        case SQLCmdType.Select:
                            mapping.DBSQLMapping.SelectSQLCommands.Add(sqlcmdproperty.SQLName, sqlcmdproperty);
                            break;
                        case SQLCmdType.Insert:
                            mapping.DBSQLMapping.InsertSQLCommands.Add(sqlcmdproperty.SQLName, sqlcmdproperty);
                            break;
                        case SQLCmdType.Update:
                            mapping.DBSQLMapping.UpdateSQLCommands.Add(sqlcmdproperty.SQLName, sqlcmdproperty);
                            break;
                        case SQLCmdType.Delete:
                            mapping.DBSQLMapping.DeleteSQLCommands.Add(sqlcmdproperty.SQLName, sqlcmdproperty);
                            break;
                    }
                }
                mapping.DBFields = fieldmappinglist;
                MappingTable.AddMapping(type, mapping);
            }
            return mapping;
        }

        public static bool Load(this Object o)
        {
            DAOMapping mapping = initMappingTable(o);
            return mapping.Load(o);
        }

        public static bool Find(this Object o)
        {
            return Find(o, string.Empty);
        }
        public static bool Find(this Object o, string selectCommandName)
        {
            DAOMapping mapping = initMappingTable(o);
            return mapping.Find(o, selectCommandName);
        }
        public static bool Find(this Object o, Conditions conditions)
        {
            DAOMapping mapping = initMappingTable(o);
            return mapping.Find(o, conditions);
        }

        private static TableAttribute getTableAttribute(Object o)
        {
            System.Reflection.MemberInfo info = o.GetType();
            TableAttribute att = (TableAttribute)Attribute.GetCustomAttribute(info, typeof(TableAttribute));
            return att;
        }

        private static SQLAttribute[] getSQLAttributes(Object o)
        {
            System.Reflection.MemberInfo info = o.GetType();
            SQLAttribute[] atts = (SQLAttribute[])Attribute.GetCustomAttributes(info, typeof(SQLAttribute));
            return atts;
        }

        private static List<FieldMapping> getFieldMappingFormPropertyInfo(PropertyInfo[] infos, ref bool alreadyHasPrimaryKey)
        {
            List<FieldMapping> list = new List<FieldMapping>();
            foreach (PropertyInfo info in infos)
            {
                FieldAttribute att = (FieldAttribute)Attribute.GetCustomAttribute(info, typeof(FieldAttribute));
                string fieldname;
                bool primarykey;
                bool autogenerate;
                bool ismapping;
                if (att != null)
                {
                    if (att.FieldName != null)
                    {
                        fieldname = att.FieldName;
                    }
                    else
                    {
                        fieldname = info.Name;
                    }
                    primarykey = att.PrimaryKey;
                    autogenerate = att.AutoGenerate;
                    ismapping = att.IsMapping;
                }
                else
                {
                    fieldname = info.Name;
                    primarykey = false;
                    autogenerate = false;
                    ismapping = true;
                }
                if (primarykey && alreadyHasPrimaryKey)
                {
                    throw new Exception("当前版本中只支持一个主键字段，不支持联合主键！");
                }
                else
                {
                    alreadyHasPrimaryKey = alreadyHasPrimaryKey ? true : primarykey;
                }
                Type fieldType = info.PropertyType;
                if (info.PropertyType.IsGenericType && info.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    // If it is NULLABLE, then get the underlying type. eg if "Nullable<int>" then this will return just "int"
                    fieldType = info.PropertyType.GetGenericArguments()[0];
                }

                FieldMapping property = new FieldMapping()
                {
                    DBFieldName = fieldname,
                    DBPrimaryKey = primarykey,
                    FieldType = fieldType,
                    DbFieldType = DbTypeConverter.TypeToDbType(fieldType),
                    FieldInfo = info,
                    IsMapping = ismapping,
                    DBAutoGenerate = autogenerate
                };
                if (property.DBPrimaryKey)
                {
                    list.Insert(0, property);
                }
                else
                {
                    list.Add(property);
                }
            }
            return list;
        }

        private static List<FieldMapping> getFieldMappingFormFieldInfo(FieldInfo[] infos, ref bool alreadyHasPrimaryKey)
        {
            List<FieldMapping> list = new List<FieldMapping>();
            foreach (FieldInfo info in infos)
            {
                FieldAttribute att = (FieldAttribute)Attribute.GetCustomAttribute(info, typeof(FieldAttribute));
                string fieldname;
                bool primarykey;
                bool autogenerate;
                bool ismapping;
                if (att != null)
                {
                    if (att.FieldName != null)
                    {
                        fieldname = att.FieldName;
                    }
                    else
                    {
                        fieldname = info.Name;
                    }
                    primarykey = att.PrimaryKey;
                    autogenerate = att.AutoGenerate;
                    ismapping = att.IsMapping;
                }
                else
                {
                    fieldname = info.Name;
                    primarykey = false;
                    autogenerate = false;
                    ismapping = true;
                }
                if (primarykey && alreadyHasPrimaryKey)
                {
                    throw new Exception("当前版本中只支持一个主键字段，不支持联合主键！");
                }
                else
                {
                    alreadyHasPrimaryKey = alreadyHasPrimaryKey ? true : primarykey;
                }

                Type fieldType = info.FieldType;
                if (info.FieldType.IsGenericType && info.FieldType.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    // If it is NULLABLE, then get the underlying type. eg if "Nullable<int>" then this will return just "int"
                    fieldType = info.FieldType.GetGenericArguments()[0];
                }

                FieldMapping property = new FieldMapping()
                {
                    DBFieldName = fieldname,
                    DBPrimaryKey = primarykey,
                    FieldType = fieldType,
                    DbFieldType = DbTypeConverter.TypeToDbType(fieldType),
                    FieldInfo = info,
                    IsMapping = ismapping,
                    DBAutoGenerate = autogenerate
                };
                if (property.DBPrimaryKey)
                {
                    list.Insert(0, property);
                }
                else
                {
                    list.Add(property);
                }
            }
            return list;
        }

        private static List<FieldMapping> getFieldMapping(Object o)
        {
            bool alreadyHasPrimaryKey = false;
            List<FieldMapping> list = new List<FieldMapping>();
            PropertyInfo[] propertyinfos = o.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            FieldInfo[] fieldinfos = o.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            list.AddRange(getFieldMappingFormPropertyInfo(propertyinfos, ref alreadyHasPrimaryKey));
            //list.AddRange(getFieldMappingFormInfo(fieldinfos, ref alreadyHasPrimaryKey));
            return list;
        }

        public static IList<T> FindList<T>(this object o)
        {
            return FindList<T>(o, string.Empty);
        }

        public static IList<T> FindList<T>(this object o, string selectCommandName)
        {
            DAOMapping mapping = initMappingTable(o);
            return mapping.FindList<T>(o, selectCommandName);
        }

        public static IList<T> FindList<T>(this object o, Conditions conditions)
        {
            DAOMapping mapping = initMappingTable(o);
            return mapping.FindList<T>(o, conditions);
        }

        /*
        public static IList<Object> FindList(this object o, int selectCommandIndex)
        {
            SQLAttribute att = null;
            SQLAttribute[] atts = getSelectCommands(o);
            if (selectCommandIndex > atts.Length - 1)
            {
                throw new Exception("Select Attribute [" + selectCommandIndex.ToString() + "] 超出范围！");
            }
            att = atts[atts.Length - selectCommandIndex - 1];
            return FindList(o, att);
        }
         */

        public static bool Insert(this Object o, string SQLName = "")
        {
            DAOMapping mapping = initMappingTable(o);
            return mapping.Insert(o, SQLName);
        }
        public static bool Update(this Object o, string SQLName = "")
        {
            DAOMapping mapping = initMappingTable(o);
            return mapping.Update(o, SQLName);
        }
        public static bool Delete(this Object o, string SQLName = "")
        {
            DAOMapping mapping = initMappingTable(o);
            return mapping.Delete(o, SQLName);
        }
        public static bool CreateTable(this Object o)
        {
            DAOMapping mapping = initMappingTable(o);
            return mapping.CreateTable(o);
        }
    }
}
